#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
@author:余振新
@file: base_driver.py
@time: 2020/01/10
"""
from time import sleep

import pywinauto
import pyautogui
import pyperclip
import pytest
from pywinauto.keyboard import send_keys
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.ui import WebDriverWait

from common.log import INFO, ERROR, WARNING


class BaseAction:
    def __init__(self, _driver):
        self.driver = _driver
        self.timeout = 10
        self.poll_frequency = 0.1
        self.wait = WebDriverWait(driver=self.driver, timeout=self.timeout, poll_frequency=self.poll_frequency)

    """
        ------------------------------------------------------------操作部分----------------------------------------------------------------------
    """

    def find_element(self, locator: tuple):
        """
        定位元素是否存在与dom树，不在乎是否可见（可见：元素高/宽度>0），速度快，性能更好
        :param: 定位器
        :return: 存在则返回WebElement，否则抛出异常并返回False
        """
        try:
            return self.wait.until(ec.presence_of_element_located(locator))
        except Exception as e:
            ERROR.logger.error(f"定位失败,错误原因为：{type(e).__name__},定位器为：{locator}")
            return False

    def find_visibility_element(self, locator: tuple):
        """
        定位元素是否存在与dom树，并且是可见（可见：元素高/宽度>0），相比find_element速度慢
        :param 定位器
        :return: 存在则返回WebElement，否则抛出异常并返回False
        """
        try:
            return self.wait.until(ec.visibility_of_element_located(locator))
        except Exception as e:
            WARNING.logger.warning(f"定位失败，错误原因为：{type(e).__name__},定位器为：{locator}")
            return False

    def find_elements(self, locator: tuple):
        """
        定位所有符合条件的元素
        :param 定位器
        :return: 存在则返回WebElement，否则抛出异常并返回False
        """
        try:
            return self.wait.until(ec.presence_of_all_elements_located(locator))
        except Exception as e:
            WARNING.logger.warning(f"定位失败，错误原因为：{type(e).__name__},定位器为：{locator}")
            return False

    def send_keys(self, locator: tuple, keys: str):
        """
        在元素中输入内容
        :param locator: 定位器
        :param keys: 需要输入的内容
        """
        sleep(0.1)
        try:
            ele = self.wait.until(ec.element_to_be_clickable(locator))
            ele.clear()
            ele.send_keys(keys)
            INFO.logger.info("输入成功！输入内容-> %s" % keys)
        except Exception as e:
            ERROR.logger.error(f"单击失败，错误原因为：{type(e).__name__},定位器为：{locator}")
            pytest.fail("捕获到异常,停止测试执行")

    def click(self, locator: tuple):
        """
        单击元素
        :param locator:  定位器
        """
        sleep(0.1)
        try:
            ele = self.wait.until(ec.element_to_be_clickable(locator))
            ele.click()
            INFO.logger.info("元素单击成功")
        except Exception as e:
            ERROR.logger.error(f"单击失败,错误原因为：{type(e).__name__},定位器为：{locator}")
            pytest.fail("捕获到异常,停止测试执行")

    def click_all(self, locator: tuple):
        """
         遍历单击所有定位到的元素
        :param locator:  定位器
        """
        eles = self.find_elements(locator)
        if not eles:
            pytest.fail("捕获到异常,停止测试执行")
        else:
            for ele in eles:
                try:
                    ele.click()
                    INFO.logger.info("元素单击成功")
                except Exception as e:
                    WARNING.logger.warning(f"单击失败,错误原因为：{type(e).__name__},定位器为：{locator}")

    def click_index(self, locator: tuple, index):
        """
        单击对应下标的元素
        :param index: 下标
        :param locator:  定位器
        """
        eles = self.find_elements(locator)
        if not eles:
            pytest.fail("捕获到异常,停止测试执行")
        else:
            try:
                ele = eles[index]
                ele.click()
                INFO.logger.info("元素单击成功")
            except Exception as e:
                ERROR.logger.error(f"单击失败,错误原因为：{type(e).__name__},定位器为：{locator}")
                pytest.fail("捕获到异常,停止测试执行")

    def double_click(self, locator: tuple):
        """
        双击元素
        :param locator:  定位器
        """
        try:
            ele = self.wait.until(ec.element_to_be_clickable(locator))
            action = ActionChains(self.driver)
            action.double_click(ele)
            action.perform()
            INFO.logger.info("元素双击成功")
        except Exception as e:
            ERROR.logger.error(f"双击失败,错误原因为：{type(e).__name__},定位器为：{locator}")
            pytest.fail("捕获到异常,停止测试执行")

    def get_text(self, locator: tuple):
        """
        获取元素中的text文本内容
        :param locator: 定位器
        :return:获取成功则返回文本内容,否则返回 False
        """
        ele = self.find_visibility_element(locator)
        if not ele:
            pytest.fail("捕获到异常,停止测试执行")
        else:
            text = ele.text
            INFO.logger.info("获取成功！获取的文本内容为-> %s" % text)
            return text

    def switch_to_frame(self, locator: tuple):
        """
        切换进入frame
        :param locator: 定位器：注意！！！！,定位器的写法为 xxx=（“By.id”,"xxx"）
        :return:切换成功则返回True,否则返回 False
        """
        try:
            self.wait.until(ec.frame_to_be_available_and_switch_to_it(locator))
            INFO.logger.info("切换Frame成功")
        except Exception as e:
            ERROR.logger.error(f"切换Frame成功失败,错误原因为：{type(e).__name__},定位器为：{locator}")
            pytest.fail("捕获到异常,停止测试执行")

    def switch_out_of_parent_frame(self):
        """
        切换回主内容框架
        :return: 切换成功则返回 True，否则返回 False
        """
        try:
            self.driver.switch_to.parent_frame()
            INFO.logger.info("切换回主内容框架成功")
            return True
        except Exception as e:
            ERROR.logger.error(f"切换回主内容框架失败，错误原因为：{type(e).__name__}")
            pytest.fail("捕获到异常，停止测试执行")

    def switch_out_of_frame(self):
        """
        切换回主内容框架
        :return: 切换成功则返回 True，否则返回 False
        """
        try:
            self.driver.switch_to.default_content()
            INFO.logger.info("切换回主内容框架成功")
            return True
        except Exception as e:
            ERROR.logger.error(f"切换回主内容框架失败，错误原因为：{type(e).__name__}")
            pytest.fail("捕获到异常，停止测试执行")

    def alert_click(self):
        """
        点击alert的确认按钮
        :return:获取成功则返回文本内容,否则返回 False
        """
        try:
            alert = self.wait.until(ec.alert_is_present())
            alert.accept()
            INFO.logger.info("alert点击成功")
        except Exception as e:
            ERROR.logger.error(f"点击alert确认按钮失败,错误原因为：{type(e).__name__}")
            pytest.fail("捕获到异常,停止测试执行")

    def get_alert_text(self):
        """
        获取alert的text文本内容
        :return:获取成功则返回文本内容,否则返回 False
        """
        alert = self.wait.until(ec.alert_is_present())
        if alert:
            text = alert.text()
            INFO.logger.info("获取alert文本内容成功")
            return text
        else:
            WARNING.logger.warning("获取alert文本内容失败")

    def switch_to_windows(self, new_title: str):
        """
        切换句柄（窗口）
        :param new_title: 新窗口的title值,用于判断是否切换成功
        :return:切换成功则返回True,否则返回 False
        """
        try:
            handles_before = self.driver.window_handles
            self.wait.until(ec.new_window_is_opened(handles_before))
            new_handle = [handle for handle in self.driver.window_handles if handle not in handles_before][0]
            self.driver.switch_to.window(new_handle)
            if self.is_title(new_title):
                INFO.logger.info("切换句柄成功")
            else:
                WARNING.logger.warning("切换句柄失败,title与新窗口不符")
                pytest.fail("捕获到异常,停止测试执行")
        except Exception as e:
            ERROR.logger.error(f"切换窗口失败,错误原因为：{type(e).__name__}")
            pytest.fail("捕获到异常,停止测试执行")

    def scroll_to_element(self, locator: tuple):
        """
        将鼠标移动到某一元素上，
        :param locator:
        :return:
        """
        action = ActionChains(self.driver)
        ele = self.find_visibility_element(locator)
        if not ele:
            pytest.fail("捕获到异常,停止测试执行")
        else:
            try:
                action.move_to_element(ele).perform()
                INFO.logger.info("移动到目标元素成功！")
            except Exception as e:
                ERROR.logger.error(f"移动到目标元素失败,错误原因为：{type(e).__name__}")
                pytest.fail("捕获到异常,停止测试执行")

    @staticmethod
    def upload_file(file_path):
        """
        上传文件
        :param file_path:
        :return:
        """
        sleep(0.4)
        INFO.logger.info("输入的文件名是 %s" % file_path)
        pyperclip.copy(file_path)
        pyautogui.hotkey('Ctrl', 'V')
        pyautogui.press("enter", 2)
        sleep(0.5)

    @staticmethod
    def upload_file_new(file_path):
        """
        上传文件
        :param file_path:
        :return:
        """
        app = pywinauto.Desktop()
        dlg = app["打开"]
        # dlg["Toolbar3"].click()
        send_keys(file_path)
        send_keys("{VK_RETURN}")
        # sleep(0.4)
        # INFO.logger.info("输入的文件名是 %s" % file_path)
        # pyperclip.copy(file_path)
        # pyautogui.hotkey('Ctrl', 'V')
        # pyautogui.press("enter", 2)
        # sleep(0.5)

    @staticmethod
    def sendkey_end():
        """
        输入键盘上的”END“键，可操作滚动条至底部
        :return:
        """
        pyautogui.press('end')

    def scroll_to_bottom_js(self, locator: tuple):
        """
        通过js的方式将元素的滚动条拉至底部
        :param locator: 需要操作的元素定位div或者body
        :return:
        """
        ele = self.find_visibility_element(locator)
        if not ele:
            ERROR.logger.error("输入内容失败,因为元素没有定位")
            raise ValueError("定位到的元素为False")
        else:
            self.driver.execute_script("arguments[0].scrollTop = arguments[0].scrollHeight", ele)
            INFO.logger.info("滚动到底部操作成功")

    def scroll_to_position_js(self, position: int):
        """
        通过js的方式将滚动条移动到对应垂直x的位置
        :param position: 垂直位置
        :return:
        """
        self.driver.execute_script("window.scrollTo(0, arguments[0]);", position)

    """
    ------------------------------------------------------------断言部分----------------------------------------------------------------------
    """

    def is_url(self, _url: str):
        """
        返回bool类型,判断网址是否符合预期,网址结尾需要多加一个“/” 否则不匹配
        :param _url: 预期的网址
        :return: Ture or False
        """
        try:
            self.wait.until(ec.url_to_be(_url))
            INFO.logger.info("True --> 网址-> %s 符合预期" % _url)
            return True
        except TimeoutException:
            WARNING.logger.warning("False --> 网址-> %s 不符合预期" % _url)
            return False

    def is_url_contains(self, _url: str):
        """
        返回bool类型,判断网址是否包含预期
        :param _url: 预期的网址
        :return: Ture or False
        """
        try:
            self.wait.until(ec.url_contains(_url))
            INFO.logger.info("True --> 网址-> %s 包含预期" % _url)
            return True
        except TimeoutException:
            WARNING.logger.warning("False --> 网址-> %s 不包含预期" % _url)
            return False

    def is_title(self, _title: str):
        """
        返回bool类型,判断标题是否符合预期
        :param _title: 预期的标题
        :return: Ture or False
        """
        try:
            self.wait.until(ec.title_is(_title))
            INFO.logger.info("True --> 标题-> %s 符合预期" % _title)
            return True
        except TimeoutException:
            WARNING.logger.warning("False --> 标题-> %s 不符合预期" % _title)
            return False

    def is_title_contains(self, _title: str):
        """
        返回bool类型,判断标题是否包含预期
        :param _title:  预期的标题部分内容
        :return:  True or False
        """
        try:
            self.wait.until(ec.title_contains(_title))
            INFO.logger.info("True --> 标题-> %s 包含预期" % _title)
            return True
        except TimeoutException:
            WARNING.logger.warning("False --> 标题-> %s 不包含预期" % _title)
            return False

    def is_text_in_element(self, locator: tuple, _text: str):
        """
        返回bool类型,判断元素文本内容是否符合预期
        :param locator:  定位器
        :param _text:  预期的文本内容
        :return:  True or False
        """
        try:
            self.wait.until(ec.text_to_be_present_in_element(locator, _text))
            INFO.logger.info("True --> 元素文本内容-> %s 符合预期" % _text)
            return True
        except TimeoutException:
            WARNING.logger.warning("Flase 元素文本内容-> %s 不符合预期" % _text)
            return False

    def is_value_in_element(self, locator: tuple, _value: str):
        """
        返回bool类型,判断元素value值是否符合预期,value为空字符串,返回False
        :param locator:  定位器
        :param _value:  元素的value值
        :return:  True or False
        """
        try:
            self.wait.until(ec.text_to_be_present_in_element_value(locator, _value))
            INFO.logger.info("True --> 元素value值-> %s 符合预期" % _value)
            return True
        except TimeoutException:
            WARNING.logger.warning("False --> 元素value值-> %s 与预期不符" % _value)
            return False

    def is_alert(self):
        """
        判断是否有alert弹出,有则返回alert对象
        :return:  alert or False
        """
        try:
            self.wait.until(ec.alert_is_present())
            INFO.logger.info("True --> 找到alert弹窗")
            return True
        except TimeoutException:
            WARNING.logger.warning("False --> 没有找到alert弹窗")
            return False

    def is_selected_locator(self, locator: tuple):
        """
        返回bool类型,判断select是否被勾选
        :param locator: 定位器
        :return: True or False
        """
        try:
            self.wait.until(ec.element_located_to_be_selected(locator))
            INFO.logger.info("True --> 对应select已被选中")
            return True
        except TimeoutException:
            WARNING.logger.warning("False --> 对应select没有被选中")
            return False

    def is_element_exist(self, locator: tuple):
        """
        返回bool类型,判断元素是否存在
        :param locator:  定位器
        :return:  True or False
        """
        ele = self.find_element(locator)
        if ele:
            INFO.logger.info("True --> 元素存在于dom")
            return True
        else:
            WARNING.logger.warning("False --> 元素不存在于dom")
            return False

    def is_element_visibility(self, locator: tuple):
        """
        返回bool类型,判断元素是否可见
        :param locator:  定位器
        :return:  True or False
        """
        ele = self.find_visibility_element(locator)
        if ele:
            INFO.logger.info("True --> 元素存在于dom")
            return True
        else:
            WARNING.logger.warning("False --> 元素不存在于dom")
            return False


if __name__ == "__main__":
    loc1 = ("id", "kw")
    loc2 = ("id", "su")
    loc3 = ("name", "tj_trnews")
    loc4 = ("link text", "搜索设置")
    loc5 = ("link text", "保存设置")
    loc6 = ("link text", "设置")
    # driver = webdriver.Chrome()
    # driver.get("http://www.baidu.com/")
    # driver.maximize_window()
    # url = driver.current_url
    # print(url)
    # ba = BaseAction(driver)
    # ba.is_url("www.baidu.com")
    # ba.is_url("https://www.baidu.com/")
    # ba.is_url_contains("1")
    # ba.is_url_contains("www")
    # ba.is_title("百度一下,你就121知道")
    # print(a)
    # print(type(a))
    # ba.is_title(r"百度一是是下,1你1就1知道")
    # ba.is_title_contains("xxx")
    # ba.is_title_contains("百度一下")
    # ba.is_value_in_element(loc2, "ssssss")
    # ba.is_value_in_element(loc2, "百度一下")
    # ba.is_text_in_element(loc3, "xxx")
    # ba.is_text_in_element(loc3, "新闻")
    # ba.is_element_exist(loc1)
    # ba.send_keys(loc1, 'woowowo')
    # ba.click(loc2)
    # ba.click(loc6)
    # ba.click(loc4)
    # ba.click(loc5)
    # alert = ba.is_alert()
    # print(alert.text)
    # alert.accept()
    # driver.quit()
